程式可讀性要高,這件事大家都知道,但是真正能做到的有幾個?在工作中,當你在撰寫一個功能時,因為你的心思全心全意都放在你的邏輯裡,你不是一個『隨便拿以前的code複製貼上就交差』的人...(吧?),所以妳寫出來的程式脈絡,不管怎麼看都是合理的。然而,別人怎麼看呢?不知道?你隨便拿一個同組的同事的code起來看看就能體會啦!
我們在前文中提過,『寫出機器看得懂的code很簡單。compile能過機器就看得懂,寫出人類看得懂的才是真功夫。』本文從另一個角度出發,如果你遇到一個邏輯比較複雜,條件比較多的判斷式,你應該怎麼寫出人類比較看得懂的程式,降低別人閱讀你的程式碼時的『WTF/min』率。
我們來看看範例吧!這個範例超簡單。我有一個妹妹,他想要交男朋友,於是就列出他的擇偶條件如下:
我就很熱心的幫他把這樣的條件寫成判斷式:
public boolean isOKAdai(boolean isReach, int facePoint,
boolean hasHouse, boolean hasCar,
boolean isPhD, long income,
boolean isPlayBoy, boolean isFunny, boolean hasHumor, boolean isGentle){
if (isReach && facePoint > 90 && hasHouse && hasCar && isPhD && income > 3000000
&& !isPlayBoy && isFunny && hasHumor && isGentle){
return true;
} else {
return false;
}
}
看得很痛苦吧?如果你感到痛苦,恭喜你有一雙正常人的眼睛。這樣的程式寫法只有機器看得懂,人類是看得很痛苦的。於是,一些比較有經驗的RD就發現,幫他們分些層次,比較能提高可讀性。為了分出層次,著名的波動拳就出現了:
public boolean isOKHadouken(boolean isRich, int facePoint,
boolean hasHouse, boolean hasCar,
boolean isPhD, long income,
boolean isPlayBoy, boolean isFunny, boolean hasHumor,
boolean isGentle) {
if (isRich) {
if (facePoint > 90) {
if (hasHouse) {
if (hasCar) {
if (isPhD) {
if (income > 3000000) {
if (!isPlayBoy) {
if (isFunny) {
if (hasHumor) {
if (isGentle) {
return true;
}
}
}
}
}
}
}
}
}
}
return false;
}
這樣的波動拳是否在你的專案中經常出現?如果是,那你要小心了。為什麽?因為,上面的例子是非常簡單的,但是在工作中常出現的其實比較像:『沒有初沒關係,只要不花心就好。』、『萬一是個花心男,那至少要有錢,不然就是要體貼
。』、『如果不體貼,那一定要有錢,或者是高學歷且幽默』...等複雜狀況。那麼,你的波動拳就沒有辦法像上面這麼順暢了。他應該會呈現『大波中有小波,else中有if』的狀況。什麼?想像不出來?明天回去公司開你自己的專案出來看看...
Robert C. Martin說,程式碼本身就要給予閱讀者足夠的資訊。所謂『足夠的資訊』包含很多,先前提過的命名是一種,這裡把條件分類提成帶語意的函式也是一種。於是我進一步分析,發現妹妹的條件其實可以概略分成四種:長得好看、物質生活高、工作表現好、個性好。於是我重新整理了一下,就像下面所述:
public boolean isOK(boolean isRich, int facePoint,
boolean hasHouse, boolean hasCar,
boolean isPhD, long income,
boolean isPlayBoy, boolean isFunny,
boolean hasHumor, boolean isGentle) {
return isGoodLooking(facePoint) &&
isGoodMaterialLife(isRich, hasHouse, hasCar) &&
isGoodWork(isPhD, income) &&
isGoodMind(isPlayBoy, isFunny, hasHumor, isGentle);
}
private boolean isGoodLooking(int facePoint) {
return 90 < facePoint;
}
private boolean isGoodMaterialLife(boolean isRich, boolean hasHouse, boolean hasCar) {
return isRich && hasHouse && hasCar;
}
private boolean isGoodWork(boolean isPhD, long income) {
return isPhD && 3000000 < income;
}
private boolean isGoodMind(boolean isPlayboy, boolean isFunny, boolean hasHumor, boolean isGentle) {
return !isPlayboy && isFunny && hasHumor && isGentle;
}
您會不會有一種感覺:『這麼一來把原本很短的的程式拉得很長,哪有好讀?』好讀多了!別忘記,我們先前說的,這是波動拳中最簡單的一種喔!如果你的波動拳是複合型的,那麼這樣的拆解並給予語意,就是很好的投資了。
另外,這樣把條件拆出來並給予語意,增加可讀性其實不是最大好處喔!增加擴充性才是最大的好處。試想,萬一今天我不是只有一個妹妹,我有很多親戚朋友都來找我介紹對象,並且大家的條件都不一樣時怎麼辦?難道你要用『co過去改一改』大法,創作出一個又一個的波動拳?別鬧了,好嗎?
抽了邏輯後,你可以使用『pattern』設計模式,搭配靜態工廠與抽象介面,他們才是立馬將你從無限copy-paste地獄中解救出來的救世主!不喜歡的話,『decorator』設計模式也是不錯的選擇。方法有很多,你可以自由搭配,但是可以確定的是,不先把邏輯抽象化,要套用好的設計模式難度會很高。
『珍惜生命,遠離波動拳』不是一句口號。要寫出能跑不會錯的程式,對職業的工程師來說太簡單了。然而,產品中長期下來最大的成本不是開發,而是維護與修改。因此,寫出好維護、好懂的程式,才是平庸工程師進階到優秀工程師的最重要轉捩點。